package complier.craft;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * 一个简单的语法解析器。
 * 能够解析简单的表达式、变量声明和初始化语句、赋值语句。
 * 它支持的语法规则为：
 * <p>
 * programm -> intDeclare | expressionStatement | assignmentStatement
 * intDeclare -> 'int' Id ( = additive) ';'
 * expressionStatement -> addtive ';'
 * addtive -> multiplicative ( (+ | -) multiplicative)*
 * multiplicative -> primary ( (* | /) primary)*
 * primary -> IntLiteral | Id | (additive)
 * 三种表达式：int 变量声明，赋值语句，表达式语句，
 */
public class SimpleParser {

    public static void main(String[] args) {
        SimpleParser parser = new SimpleParser();
        String script = null;
        ASTNode tree = null;


        try {
            System.out.println("------------------");
            script = "int age = 45+2; age= 20; age+10*2;";
            System.out.println("解析" + script);
            tree = parser.parse(script);
            parser.dumpAST(tree, "");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

        //测试异常语法
        try {
            script = "2+3+;";
            System.out.println("解析：" + script);
            tree = parser.parse(script);
            parser.dumpAST(tree, "");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

        //测试异常语法
        try {
            script = "2+3*;";
            System.out.println("解析：" + script);
            tree = parser.parse(script);
            parser.dumpAST(tree, "");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }

    /**
     * 执行脚本，并打印输出AST和求值过程。
     *
     * @param script
     */
    private void evaluate(String script) {
        try {
            ASTNode tree = parse(script);
            dumpAST(tree, "");
            evaluate(tree, "");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     * 对某个AST节点求值，并打印求值过程。
     *
     * @param node
     * @param indent 打印输出时的缩进量，用tab控制
     * @return
     */
    private int evaluate(ASTNode node, String indent) {
        int result = 0;
        System.out.println(indent + "Calculating:" + node.getType());

        switch (node.getType()) {
            case Program:
                for (ASTNode child : node.getChildren()) {
                    result = evaluate(child, indent + "\t");
                }
                break;
            case Additive:
                ASTNode child1 = node.getChildren().get(0);
                int av1 = evaluate(child1, indent + "\t");
                ASTNode child2 = node.getChildren().get(1);
                int av2 = evaluate(child2, indent + "\t");
                if (node.getText().equals("+")) {
                    result = av1 + av2;
                } else {
                    result = av1 - av2;
                }
                break;
            case Multiplicative:
                ASTNode node1 = node.getChildren().get(0);
                int mv1 = evaluate(node1, indent + "\t");
                ASTNode node2 = node.getChildren().get(1);
                int mv2 = evaluate(node2, indent + "\t");
                if (node.getText().equals("*")) {
                    result = mv1 * mv2;
                } else {
                    result = mv1 - mv2;
                }
                break;
            case IntLiteral:
                result = Integer.parseInt(node.getText());
                break;
        }
        System.out.println(indent + "Result:" + result);
        return result;
    }

    public ASTNode parse(String script) throws Exception {
        SimpleLexer lexer = new SimpleLexer();
        TokenReader tokens = lexer.tokenize(script);

        ASTNode rootNode = prog(tokens);
        return rootNode;
    }

    private ASTNode prog(TokenReader tokens) throws Exception {
        // + 表达式,赋值语句,int 声明
        SimpleASTNode root = new SimpleASTNode(ASTNodeType.Program, "pwc");

        while (tokens.peek() != null) {
            SimpleASTNode child = initDeclare(tokens);

            if (child == null) {
                child = expressionStatement(tokens);
            }

            if (child == null) {
                child = AssignmentStatement(tokens);
            }

            if (child != null) {
                root.addChild(child);
            }
        }
        return root;
    }


    /**
     * 赋值语句，如age = 10*2;
     *
     * @return
     * @throws Exception
     */
    private SimpleASTNode AssignmentStatement(TokenReader tokens) throws Exception {
        // 首先读标志符，读=，读表达式，否回吐一个符号
        SimpleASTNode node = null;
        Token token = tokens.peek();
        if (token != null && token.getType() == TokenType.Identifier) {
            token = tokens.read();// 取标注符
            node = new SimpleASTNode(ASTNodeType.AssignmentStmt, token.getText());
            token = tokens.peek();
            if (token != null && token.getType() == TokenType.Assignment) {
                tokens.read();// 取=
                SimpleASTNode child = additive(tokens);
                if (child == null) {// 表达式不完整
                    throw new Exception("invalide assignment statement, expecting an expression");
                } else {
                    token = tokens.peek();
                    if (token != null && token.getType() == TokenType.SemiColon) {
                        tokens.read();
                        node.addChild(child);
                    } else {// 没有结束符号
                        throw new Exception("invalid statement, expecting semicolon");
                    }
                }
            } else {
                tokens.unread();
                node = null;
            }
        }
        return node;
    }


    /**
     * 表达式语句，即表达式后面跟个分号。
     * 1+2+3;
     *
     * @return
     * @throws Exception
     */
    private SimpleASTNode expressionStatement(TokenReader tokens) throws Exception {
        int pos = tokens.getPosition();
        SimpleASTNode node = additive(tokens);
        if (node != null) {
            Token token = tokens.peek();
            if (token != null && token.getType() == TokenType.SemiColon) {
                tokens.read(); // 消耗掉分号到下一句
            } else {
                node = null;
                tokens.setPosition(pos);// 回溯
            }
        }
        return node;
    }

    /**
     * 整型变量声明，如：
     * int a;
     * int b = 2*3;
     *
     * @return
     * @throws Exception
     */
    private SimpleASTNode initDeclare(TokenReader tokens) throws Exception {
        // 首先判断是否是int 表达式 目前仅支持int
        // 逐层读取 是否 = 读取产生式 -> 加法计算 目前只支持加法
        // 加法产生的node 判断是否为空 空抛错
        // 校验结尾是否是; 否 抛错
        SimpleASTNode node = null;
        Token token = tokens.peek(); //预读 int
        if (token != null && token.getType() == TokenType.Int) {
            tokens.read();// 消耗int
            token = tokens.peek(); //预读标志字符串
            if (token.getType() == TokenType.Identifier) {
                token = tokens.read();
                // 标志字符串
                node = new SimpleASTNode(ASTNodeType.IntDeclaration, token.getText());
                token = tokens.peek();//预读 =
                if (token != null && token.getType() == TokenType.Assignment) {
                    tokens.read();//消耗=
                    SimpleASTNode child = additive(tokens);
                    if (child == null) {
                        throw new Exception("invalide variable initialization, expecting an expression");
                    } else {
                        node.addChild(child);
                    }
                }
            } else {
                throw new Exception("variable name expected");
            }

            if (node != null) {
                token = tokens.peek();
                if (token != null && token.getType() == TokenType.SemiColon) {
                    tokens.read();
                } else {
                    throw new Exception("invalid statement, expecting semicolon");
                }
            }
        }
        return node;
    }

    /**
     * 语法解析：加法表达式
     *
     * @return
     * @throws Exception
     */
    private SimpleASTNode additive(TokenReader tokens) throws Exception {
        // 消耗  加法表达式左边
        SimpleASTNode child1 = multiplicative(tokens);
        SimpleASTNode node = child1;

        Token token = tokens.peek(); // 预读 + -
        if (node != null && token != null) {
            if (token.getType() == TokenType.Minus || token.getType() == TokenType.Plus) {
                token = tokens.read(); // 消耗 + -
                SimpleASTNode child2 = additive(tokens);
                if (child2 != null) {
                    node = new SimpleASTNode(ASTNodeType.Additive, token.getText());
                    node.addChild(child1);
                    node.addChild(child2);
                } else {
                    throw new Exception("invalid additive expression, expecting the right part.");
                }
            }
        }
        return node;
    }


    /**
     * 语法解析：乘法表达式
     *
     * @return
     * @throws Exception
     */
    private SimpleASTNode multiplicative(TokenReader tokens) throws Exception {
        // 消耗乘法 左边
        SimpleASTNode child1 = primary(tokens);
        SimpleASTNode node = child1;

        Token token = tokens.peek(); // 预读 * / 符号
        // 如果child1 不空，child1 = primary(tokens);已经处理到对应的表达式位置，往下就是乘法的另一半
        if (node != null && token != null) {
            if (token.getType() == TokenType.Slash || token.getType() == TokenType.Star) {
                token = tokens.read();//读符号 / *
                SimpleASTNode child2 = multiplicative(tokens); //  消耗 乘法 右边
                if (child2 != null) {
                    node = new SimpleASTNode(ASTNodeType.Multiplicative, token.getText());
                    node.addChild(child1);
                    node.addChild(child2);
                } else {
                    throw new Exception("invalid multiplicative expression, expecting the right part.");
                }
            }
        }
        return node;
    }

    /**
     * 语法解析：基础表达式
     *
     * @return
     * @throws Exception
     */
    private SimpleASTNode primary(TokenReader tokens) throws Exception {
        // int字面量 标志符 （exp）
        SimpleASTNode node = null;
        Token token = tokens.peek(); // 预读基础表达式
        if (token != null) {
            if (token.getType() == TokenType.IntLiteral) {
                token = tokens.read();
                node = new SimpleASTNode(ASTNodeType.IntLiteral, token.getText());
            } else if (token.getType() == TokenType.Identifier) {
                token = tokens.read();
                node = new SimpleASTNode(ASTNodeType.Identifier, token.getText());
            } else if (token.getType() == TokenType.LeftParen) {
                tokens.read(); // 消耗 （
                node = additive(tokens); // 消耗 exp
                if (node != null) {
                    token = tokens.peek();
                    if (token != null && token.getType() == TokenType.RightParen) {
                        tokens.read(); //消耗 ）
                    } else {
                        throw new Exception("expecting right parenthesis");
                    }
                } else {
                    throw new Exception("expecting an additive expression inside parenthesis");
                }
            }
        }
        return node;
    }


    private class SimpleASTNode implements ASTNode {
        SimpleASTNode parent = null;
        List<ASTNode> children = new ArrayList<>();
        List<ASTNode> readonlyChildren = Collections.unmodifiableList(children);
        ASTNodeType nodeType = null;

        String text = null;

        public SimpleASTNode(ASTNodeType nodeType, String text) {
            this.nodeType = nodeType;
            this.text = text;
        }

        @Override
        public ASTNode getParent() {
            return parent;
        }

        @Override
        public List<ASTNode> getChildren() {
            return children;
        }

        @Override
        public ASTNodeType getType() {
            return nodeType;
        }

        @Override
        public String getText() {
            return text;
        }

        public void addChild(SimpleASTNode child) {
            this.children.add(child);
        }
    }

    /**
     * 打印输出AST的树状结构
     *
     * @param node
     * @param indent 缩进字符，由tab组成，每一级多一个tab
     */
    public void dumpAST(ASTNode node, String indent) {
        System.out.println(indent + node.getType() + " " + node.getText());
        for (ASTNode child : node.getChildren()) {
            dumpAST(child, indent + "\t");
        }
    }
}
